#include "display.h"
#include "my_font.h"
#include <string.h>

CRGB _leds[MATRIX_NUM_LEDS];

Displayer::Displayer(/* args */)
{
  FastLED.addLeds<WS2812B, MATRIX_DATA_PIN, GRB>(this->leds, MATRIX_NUM_LEDS); // GRB ordering is typical
  FastLED.setBrightness(MATRIX_BRIGHTNESS);                                    // 初始亮度（0-255)
  FastLED.clear();
}

Displayer::~Displayer()
{
}

#define COOLING 55
// SPARKING: What chance (out of 255) is there that a new spark will be lit?
// Higher chance = more roaring fire.  Lower chance = more flickery fire.
// Default 120, suggested range 50-200.
#define SPARKING 120
#define FRAMES_PER_SECOND 60
void Displayer::Example_Fire()
{
  bool gReverseDirection = false;

  while (1)
  {
    // Array of temperature readings at each simulation cell
    static uint8_t heat[MATRIX_NUM_LEDS];

    // Step 1.  Cool down every cell a little
    for (int i = 0; i < MATRIX_NUM_LEDS; i++)
    {
      heat[i] = qsub8(heat[i], random8(0, ((COOLING * 10) / MATRIX_NUM_LEDS) + 2));
    }

    // Step 2.  Heat from each cell drifts 'up' and diffuses a little
    for (int k = MATRIX_NUM_LEDS - 1; k >= 2; k--)
    {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
    }

    // Step 3.  Randomly ignite new 'sparks' of heat near the bottom
    if (random8() < SPARKING)
    {
      int y = random8(7);
      heat[y] = qadd8(heat[y], random8(160, 255));
    }

    // Step 4.  Map from heat cells to LED colors
    for (int j = 0; j < MATRIX_NUM_LEDS; j++)
    {
      CRGB color = HeatColor(heat[j]);
      int pixelnumber;
      if (gReverseDirection)
      {
        pixelnumber = (MATRIX_NUM_LEDS - 1) - j;
      }
      else
      {
        pixelnumber = j;
      }
      this->leds[pixelnumber] = color;
    }

    FastLED.show(); // display this frame
    FastLED.delay(1000 / FRAMES_PER_SECOND);
  }
}

void Displayer::Example_Marquee()
{
  for (int i = 0; i < MATRIX_NUM_LEDS; i++)
  {
    this->leds[i] = CRGB::Red;
    FastLED.show();
    delay(50);
    this->leds[i] = CRGB::Black;
  }
}

void Displayer::Display(int d[], int length, CRGB color = CRGB::White)
{
  for (int i = 0; i < length; i++)
  {
    this->leds[d[i]] = color;
  }
  FastLED.show();
}

void Displayer::Clear()
{
  for (int i = 0; i < MATRIX_NUM_LEDS; i++)
  {
    this->leds[i] = CRGB::Black;
  }
  FastLED.show();
}
/// @brief ///////////////////////////////////////////////////////////////////

MatrixDisplayer *displayManager = nullptr;

MatrixDisplayer::MatrixDisplayer() : FastLED_NeoMatrix(_leds, MATRIX_WIDTH, MATRIX_HEIGHT,
                                                       NEO_MATRIX_TOP + NEO_MATRIX_LEFT +
                                                           NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG)
{
  FastLED.addLeds<NEOPIXEL, MATRIX_DATA_PIN>(_leds, MATRIX_NUM_LEDS);

  this->init();
}

MatrixDisplayer::~MatrixDisplayer()
{
}

void MatrixDisplayer::init()
{
  this->begin();
  this->setTextWrap(false);
  this->setBrightness(MATRIX_BRIGHTNESS);
  this->setFont(&Picopixel);
  // note:颜色必须要这样用法，否则会显示异常
  // 源码：  void setTextColor(uint16_t c)
  this->setTextColor(this->Color24to16(CRGB::Purple));
  this->setTextSize(1); // 默认值
  this->clear();
}

void MatrixDisplayer::show(void)
{
  if (this->enable_show)
    FastLED_NeoMatrix::show();
}

void MatrixDisplayer::SetShowEnable(bool enable_show)
{
  this->enable_show = enable_show;
}

void MatrixDisplayer::RestoreLeds()
{
  FastLED.addLeds<NEOPIXEL, MATRIX_DATA_PIN>(_leds, MATRIX_NUM_LEDS);
  this->newLedsPtr(_leds); // 恢复原始LED指针
  this->init();
}

void MatrixDisplayer::SetLeds(CRGB *new_leds)
{
  FastLED.addLeds<NEOPIXEL, MATRIX_DATA_PIN>(new_leds, MATRIX_NUM_LEDS);
  this->newLedsPtr(new_leds);
  this->init();
}

void MatrixDisplayer::Example_Neo_Print()
{
  static int xx = MATRIX_WIDTH;

  this->clear();
  this->setCursor(xx, 0); // Start at top-left corner
  this->print("Hello, world!");
  this->show();
  if (--xx < -1 * MATRIX_WIDTH - 20)
    xx = MATRIX_WIDTH;
}

void MatrixDisplayer::ShowMessage(char *message, int len, uint32_t color)
{
  this->clear();
  this->setTextColor(color);
  if (len < 1 || len > 8)
    return;
  this->setCursor(1 + (8 - len) * 2, 5); // 设置鼠标光标的位置用以确定在哪里显示文字

  this->print(message);
  this->show();
}

void MatrixDisplayer::Print(char *s, int x, int y, CRGB color)
{
  this->setTextColor(this->Color24to16((uint32_t)color));
  this->setCursor(x, y);
  this->print(s);
}

void MatrixDisplayer::PixelAnimation(int16_t ***frames, int frame_size, int frames_num, int interval, CRGB color = CRGB::White)
{
  for (int i = 0; i < frames_num; i++)
  {
    for (int j = 0; j < frame_size; j++)
    {
      // leds[frames[i][j]] = color;
      this->drawPixel(frames[i][j][0], frames[i][j][1], color);
    }
    this->show();
    delay(interval);
    for (int j = 0; j < frame_size; j++)
    {
      // leds[frames[i][j]] = CRGB::Black;
      // 奇怪，这里用CRGB::Black会报错，必须用this->Color(0, 0, 0)
      this->drawPixel(frames[i][j][0], frames[i][j][1], this->Color(0, 0, 0));
    }
    this->show();
  }
}

void MatrixDisplayer::PrintAnimation(char *s, int start_x, int end_x, int *x, CRGB color = CRGB::White)
{
  // 这里确保显示全，按照每个字符长度占用4个像素x宽计算
  int cal_content_length = strlen(s) * 4;

  displayManager->setTextColor(displayManager->Color24to16((uint32_t)color));
  displayManager->setCursor((*x), 5);
  displayManager->print(s);

  *x = *x - 1;
  // Serial.printf("%d %d %d\n", *x, end_x, strlen(s));
  // note:这里一开始有个bug
  // strlen() 返回 size_t 类型（无符号整数），当乘以 -1 时会发生无符号数下溢，导致结果变为一个极大的正数而非预期的负数。
  // 例如当 s 长度为2时， -1 * strlen(...) 实际计算为 4294967294 （32位系统），导致 31 < 4294967294 始终为 true 。
  // 解决方法：将 strlen(...) 转换为有符号整数，例如使用 (int)strlen(...) 。
  if (*x < (end_x - (cal_content_length - (start_x - end_x))))
    *x = start_x;
}

void MatrixDisplayer::PrintAnimation(char *s[], int start_x, int end_x, int *x, int offsets[], CRGB colors[], int length)
{
  int cal_content_length = offsets[length - 1] + strlen(s[length - 1]) * 4;

  for (int i = 0; i < length; i++)
  {
    displayManager->setTextColor(displayManager->Color24to16((uint32_t)colors[i]));
    displayManager->setCursor((*x) + offsets[i], 5);
    displayManager->print(s[i]);
  }

  *x = *x - 1;
  if (*x < (end_x - (cal_content_length - (start_x - end_x))))
    *x = start_x;
}

void MatrixDisplayer::DrawIcon(int16_t x, int16_t y, const image_t *img)
{
  // 背景色纯黑，确保不会被覆盖
  // 要后调用才可以
  // img->width + 1 用来分割icon和content
  this->fillRect(x, y, img->width + 1, img->height, this->Color24to16((uint32_t)CRGB::Black));

  for (int i = 0; i < img->layers_num; i++)
    this->drawBitmap(x, y,
                     img->layers[i].data,
                     img->width, img->height,
                     this->Color24to16((uint32_t)img->layers[i].color));
}

void MatrixDisplayer::DrawIconAnimation(int16_t x, int16_t *y, int16_t end_y, const image_t *img)
{
  this->DrawIcon(x, *y, img);
  if (*y < end_y)
    (*y)++;
}


void MatrixDisplayer::DrawBitmapRawRGB(int16_t x, int16_t y, CRGB led[MATRIX_NUM_LEDS] = _leds)
{
  for (int16_t i = 0; i < MATRIX_HEIGHT; i++)
  {
    for (int16_t j = 0; j < MATRIX_WIDTH; j++)
    {

      // this->setPassThruColor(led[this->XY(j, i)]);
      // Framebuffer_GFX::drawPixel(int16_t(x + j), int16_t(y + i), uint16_t(0)); // color参数不会生效，因为这里设置了passThruColor
      Framebuffer_GFX::drawPixel(int16_t(x + j), int16_t(y + i), led[this->XY(j, i)]);
    }
  }
  // this->setPassThruColor(); // close
}

void MatrixDisplayer::Example_Animation()
{
  // 动态分配三维数组
  int16_t ***frames = new int16_t **[3];
  for (int i = 0; i < 3; i++)
  {
    frames[i] = new int16_t *[1];
    frames[i][0] = new int16_t[2]{i * 2, i * 2}; // 每个坐标包含x,y两个值
  }
  this->PixelAnimation(frames, 1, 3, 1000, CRGB::Purple);
  // 释放内存
  for (int i = 0; i < 3; i++)
  {
    delete[] frames[i][0];
    delete[] frames[i];
  }
  delete[] frames;
}

// 测试用8x8位图（存储在PROGMEM中）
const uint8_t test_bitmap[] PROGMEM = {
    0x00, 0x00, 0x00, 0x00,
    0x01, 0x00, 0x02, 0x00,
    0x00, 0xE0, 0x0C, 0x00,
    0x00, 0x10, 0x18, 0x00,
    0x00, 0x60, 0x18, 0x00,
    0x01, 0xC0, 0x07, 0x00,
    0x00, 0x0F, 0x80, 0x00,
    0x00, 0x00, 0x00, 0x00};

void MatrixDisplayer::Example_Neo_Image()
{
  this->clear();

  this->drawBitmap(0, 0, test_bitmap, 32, 8, this->Color24to16(CRGB::Red)); // 绘制位图
  this->show();
  delay(1000); // 显示1秒
}

////////////////////////////////
// 从CRGB数组读取显示值并转换为16位RGB565位图
// note:这里有意思！！！bitmap是按照先x后y的顺序扫描的，和CRGB数组内的值要做索引转化
// note2:这里如果结合drawRGBBitmap函数使用会存在一个bug：所用到的几个库，除了fastled是24位全量RGB色，其他都是阉割的16位RGB565色，
// 所以上层做了颜色降级，底层又做了颜色扩展，但是经过降级和扩展后颜色会失真，即不会得到原本的24位RGB色，
// 所以这里从底层led获取的RGB色经过一顿折腾会变暗色，造成动画切换时亮度变暗，其实就是颜色失真了
const uint16_t *getDisplayBitmap16(CRGB led[])
{
  if (led == nullptr)
    led = _leds;

  static uint16_t bitmap[MATRIX_NUM_LEDS];
  for (int i = 0; i < MATRIX_HEIGHT; i++)
  {
    for (int j = 0; j < MATRIX_WIDTH; j++)
    {
      // if (i == 0 && j == 0)
      // Serial.printf("pp %x\r\n", displayManager->Color24to16((uint32_t)led[displayManager->XY(j, i)]));
      bitmap[i * MATRIX_WIDTH + j] = displayManager->Color24to16((uint32_t)led[displayManager->XY(j, i)]);
    }
  }
  return bitmap;
}
